

import gzip
import json
import time
import http.client
from PIL import Image, ImageColor, ImageDraw,ImageFont
import random

class DataModel:
    def __init__(self,initstr):
        d=initstr.split(',')

        #1 7 8 9 11 13 19
        self.day, self.init, self.max, self.min, self.close, self.vol, self.num=d[0],float(d[1]),float(d[2]),float(d[3]),float(d[4]),float(d[5]),float(d[6])
    def __repr__(self):
        return "DataModel [day:%s,init:%s,max:%s,min:%s,close:%s,vol:%s,money:%s]"\
               %(self.day,self.init,self.max,self.min,self.close,self.vol,self.num)
    def isBull(self):
        return ((float(self.close)-float(self.init))>0)


class StockDataFtecher:

    def __init__(self):
        self.saveFileName="aa.jpg"
        self.padding = {
            'Left': 40,
            'Right': 10,
            'Top': 10,
            'Bottom': 30
        }
        self.stockCode="1A0001"
        #self.stockCode="600789"
        # self.stockCode="000063"
        #self.stockCode="600460"
        self.cached = True
        self.update = not self.cached
        self.bar = (3, 1)
        self.frameWorkColor = ImageColor.getrgb("gray")
        self.frameWorkText = ImageColor.getrgb("black")
        self.backgroundColor = ImageColor.getcolor("#fff", mode='RGB')
        self.bullColor = ImageColor.getcolor("red", mode="RGB")
        self.bellColor = ImageColor.getcolor("green", mode="RGB")
        self.font =ImageFont.truetype("/System/Library/Fonts/STHeiti Light.ttc");

        prev=list(map(lambda item: "%d" % (item), range(1999, 2021)))
        prev.append("today")
        self.datarange=prev
        #self.datarange=("2020","today")


        """宽度为-1表示要通过计算得到的"""
        self.imSize = (-1, 2000)
        self.minMax=[0,0,0,0,(0,0)]
        #datas = list(filter(lambda d: int(d.day) > 20190101, datas))
        #self.daterange=("20190101",20200101)

        """
        y轴，没一个刻度对应的点数量
        这个值通常由于stepNum决定，stepNum表示需要多少个刻度来展示
        一个范围区间 min ~ max
        需要n个刻度 -> (min-max)/n=每一个刻度对应吃掉的度
        
        """
        self.numofeachStep=-1;
        self.stepNum=4
        #self.fiveDate=[0,0,0,0]
        pass

    def __v4Orv6(self):
        if self.stockCode.startswith("1A"):
            return ("v4","zs")
        if self.stockCode.startswith("600"):
            return ("v6","hs")
        if self.stockCode.startswith("300"):
            return ("v6", "hs")
        if self.stockCode.startswith("000"):
            return ("v6", "hs")

    def fetchDataByUrl(self, year):
        conn = http.client.HTTPConnection("d.10jqka.com.cn");

        type=self.__v4Orv6()

        requestStr="/%s/line/%s_%s/01/%s.js" % (type[0], type[1], self.stockCode, year)
        conn.request("GET", requestStr,
                     headers={
                         "Referer": "http://q.10jqka.com.cn/zs/detail/code/%s/"%(self.stockCode),
                         "Accept-Encoding": "gzip,deflate"
                     })
        resp = conn.getresponse()
        print("URL:%s --> %d"%(requestStr,resp.code))
        text = gzip.decompress(resp.read()).decode('utf-8')
        return text

    def fetchDataToList(self,year):
        text = self.fetchDataByUrl(year)
        jsonline=text[text.find("(") + 1:text.rfind(")")]
        if not jsonline:
            return None;
        jsonobj = json.loads(jsonline)

        dataarr=[]
        if year =="today":
            zsdata=jsonobj['%s_%s'%(self.__v4Orv6()[1],self.stockCode)]
            self.todaydata=zsdata
            if self.update:
                todaycache=open("today.cache",'w+');
                todaycache.write(json.dumps(self.todaydata))
                todaycache.close()
            line='%s,%s,%s,%s,%s,%s,%s'%(zsdata['1'],zsdata['7'],zsdata['8'],zsdata['9'],zsdata['11'],zsdata['13'],zsdata['19'],)
            dataarr.append(line)
            pass
        else:
            data = jsonobj['data']
            dataarr = data.split(';');
            pass
        # 日期，开盘，最高，最低，收盘，成交量，成交额，
        return dataarr


    def fetchData(self):
        cached=self.cached
        update=self.update
        datasall=[]
        dataModels=[]
        if not cached :
            for year in self.datarange:
                print('get data of %s of year:%s'%(self.stockCode,year))
                data=self.fetchDataToList("%s" % (year))
                if time.strftime("%Y")==year:
                    data=list(filter(lambda item:not time.strftime("%Y%m%d") == item.split(',')[0], data))
                if data != None:
                    datasall.extend(data)
            if update:
                cachefile=open('data.cache','w+')
                cachefile.write(json.dumps(datasall))
                cachefile.close()
        else:
            cachefile=open('data.cache','r+')
            datasall=json.loads(cachefile.read())
            cachefile.close()

            today=open('today.cache','r+')
            todaydata=json.loads(today.read())
            self.todaydata=todaydata
            today.close()


        for idx in range(0,len(datasall)):
            try:
                dataModels.append(DataModel(datasall[idx]))
            except ValueError:
                print("erorr at line:",datasall[idx])
                pass



        return dataModels


    def __initMinMax(self,data):
        maxV = 0
        minV = 0
        maxVol = 0
        maxNum=0
        maxVolDivMaxNum=0
        minVolDivMaxNum=0

        for idx in range(0, len(data) ):
            d=data[idx]
            ndv=float(d.num) / float(d.vol)
            if idx==0:
                minVolDivMaxNum=ndv;

            if float(d.max) > maxV:
                maxV = d.max
            if float(d.min) < minV or idx==0:
                minV = d.min
            if float(d.vol) >maxVol:
                maxVol=d.vol
            if float(d.num)>maxNum:
                maxNum=d.num
            if ndv>maxVolDivMaxNum:
                if d.day == "20031029":
                    continue
                maxVolDivMaxNum=round(ndv,2)
            if ndv<minVolDivMaxNum:
                minVolDivMaxNum=round(ndv,2)

        if self.numofeachStep==-1:
            self.numofeachStep=abs(minV-maxV)/self.stepNum

        self.minMax=(minV,maxV,maxVol,maxNum,(maxVolDivMaxNum,minVolDivMaxNum))
        print(self.minMax)



    def __drawDate(self,data, draw,  padding, bar):
        barwidth = bar[0]
        barspacer = bar[1]
        imageHeight = self.imSize[1]
        fixsize=46+10
        for idx in range(1, int(len(data)),(int)(fixsize/(barwidth+barspacer))):
            d = data[idx]
            x = idx * (barwidth + barspacer) + padding['Left'] + 2+barwidth/2
            y = imageHeight-padding['Bottom']
            for y in range(padding['Top']+2,y,2):
                draw.point((x,y),fill='gray')
            top=(x,y+4)
            draw.polygon(top+(top[0]-2,top[1]+4)+(top[0]+2,top[1]+4),fill='gray')
            draw.text((x-20,y+10),d.day,fill='black');
        pass


    def drawImage(self,data):
        self.__initMinMax(data)
        padding=self.padding
        bar=self.bar

        if self.imSize[0] == -1:
            imgWidCal=len(data)*(bar[0]+bar[1])+padding['Left']+padding['Right']+30
            self.imSize=(imgWidCal,2000)

        img = Image.new('RGB', self.imSize, self.backgroundColor)
        draw = ImageDraw.Draw(img)
        draw.line((padding['Left'], padding['Top']) + (padding['Left'], img.height - padding['Bottom']), fill=self.frameWorkColor)
        draw.line((img.width-padding['Right'], padding['Top']) + (img.width-padding['Right'], img.height - padding['Bottom']), fill=self.frameWorkColor)
        draw.line((padding['Left'], img.height-padding['Bottom']) + (img.width-padding['Right'], img.height-padding['Bottom']), fill=self.frameWorkColor)


        """Draw Framework"""
        self.__drawFrameWork(draw, self.frameWorkColor, self.frameWorkText, self.imSize, self.minMax, padding)

        """Draw BAR"""
        self.__drawBar(self.bellColor, self.bullColor, data, draw, self.imSize, self.minMax,padding,bar)
        #self.__drawLine(self.averageData(),draw,)

        """Draw Vol"""
        #self.__drawVol(self.bellColor,self.bullColor,data,draw,self.imSize,self.minMax,padding,bar)
        self.__drawVolDivNum(self.bellColor,self.bullColor,data,draw,self.imSize,self.minMax,padding,bar)

        """Draw Date"""
        self.__drawDate(data,draw,padding,bar)


        self.__drawLine(datas=data,draw=draw,days=5,color='black')
        self.__drawLine(datas=data,draw=draw,days=10,color='yellow')
        self.__drawLine(datas=data,draw=draw,days=20,color='purple')
        self.__drawLine(datas=data,draw=draw,days=30,color='green')
        self.__drawLine(datas=data,draw=draw,days=60,color='gray')
        self.__drawLine(datas=data,draw=draw,days=120,color='darkblue')

        self.__drawInfo(draw,self.minMax)
        img.show();
        if self.saveFileName:
            img.save(self.saveFileName)
        img.close()





    def __drawVol(self,bellColor,bullColor,data,draw,imSize,minMax,padding,bar):
        barspacer = bar[1]
        barwidth=self.bar[0]
        maxVol=minMax[2]
        imageHeight=self.imSize[1]
        draw.text(self.__getVolLeftTop(),text=str(round(maxVol/1000/1000/1000,2))+'亿',fill=self.frameWorkText,font=self.font)
        for idx in range(0, len(data)):
            d = data[idx]
            volRealHeight=d.vol/maxVol*self.__getVolAvailHeight()
            x=self.__getVolX(idx)
            width = x + barwidth - 1
            y=imageHeight-padding['Bottom']-2
            height=y-volRealHeight
            draw.rectangle((x,y)+(width,height), fill="white" if d.isBull() else bellColor,
                           outline=(bullColor if d.isBull() else bellColor))

    def __drawVolDivNum(self, bellColor, bullColor, data, draw, imSize, minMax, padding, bar):
        barspacer = bar[1]
        barwidth = self.bar[0]
        maxVol = minMax[4][0]
        minVol = minMax[4][1]
        imageHeight = self.imSize[1]
        draw.text(self.__getVolLeftTop(), text='金额\n成交\n量比\n%s'%(str(round(maxVol , 2)) ) ,
                  fill=self.frameWorkText, font=self.font)
        for idx in range(0, len(data)):
            d = data[idx]
            # 0.2   0.1  0.3

            #min 0.1
            #max 0.3
            # (0.2-min)/max
            #
            volRealHeight = (d.num/d.vol-minVol) / (maxVol-minVol) * self.__getVolAvailHeight()
            #volRealHeight = (d.num/d.vol) / (maxVol) * self.__getVolAvailHeight()
            x = self.__getVolX(idx)
            width = x + barwidth - 1
            y = imageHeight - padding['Bottom'] - 2
            height = y - volRealHeight
            draw.rectangle((x, y) + (width, height), fill="white" if d.isBull() else bellColor,
                           outline=(bullColor if d.isBull() else bellColor))

    def __getVolLeftTop(self):
        top = self.imSize[1] - self.padding['Bottom'] - self.__getVolAvailHeight()+5
        left=(self.bar[0]+self.bar[1])+self.padding['Left']-30
        return (left,top)

    def __getVolX(self,idx):
        x = idx * (self.bar[0] + self.bar[1]) + self.padding['Left'] + 2
        return x

    def __getVolAvailHeight(self):
        imageHeight = self.imSize[1]
        availableHeight = (imageHeight - self.padding['Top'] - self.padding['Bottom']) * 0.2 - 4
        return availableHeight


    def __drawLine(self, datas, draw,days,color):
        ndayDatas=[]
        for i in range(0,days):
            ndayDatas.append(0)
        for idx in range(days-1, len(datas)):
            sum = 0
            for i in range(idx - days+1, idx + 1):
                sum += float(datas[i].close)
                sum = round(sum, 2)
            ndayDatas.append(round(sum / days, 2))



        prev=(0,0)
        for idx in range(0, len(ndayDatas)):
            d = ndayDatas[idx]
            x=self.__getXposUsingIdx(idx)
            y=self.__getYPosUsingPoint(d)
            #y = self.padding['Top'] + availableHeight - float((d - min) * stepHeight / self.numofeachStep)
            #draw.point((x,y),fill='red')
            if idx >days:
                draw.line(prev+(x,round(y,2)),fill=color)
            prev=(x,round(y,2))
            #draw.text((x,y),text="1",fill='red')
        pass

    def __getYPosUsingPoint(self,point):
        availableHeight = (self.imSize[1] - self.padding['Top'] - self.padding['Bottom']) * 0.8
        stepHeight = int(availableHeight / self.stepNum)
        min=self.minMax[0]
        y = self.padding['Top'] + availableHeight - float((point - min) * stepHeight / self.numofeachStep)
        return y

    def __getXposUsingIdx(self,idx):
        x = idx * (self.bar[0] + self.bar[1]) + self.padding['Left'] + 2
        return x

    def __drawPoint(self,datas):
        list=[]
        for idx in range(0,len(datas)):
            d=datas[idx]
            val=0
            if d.init>d.close:
                val=d.init
            else:
                val=d.close



    def __drawBar(self,bellColor, bullColor, data, draw, imSize, minMax, padding,bar):
        barwidth = bar[0]
        barspacer = bar[1]
        imageHeight=imSize[1]
        availableHeight = (imageHeight - padding['Top'] - padding['Bottom'])*0.8

        min=minMax[0]
        max=minMax[1]
        step = self.stepNum##int((max - min) / self.numofeachStep)

        stepHeight = int(availableHeight / step)

        for idx in range(0, len(data)):
            d = data[idx]
            x = idx * (barwidth + barspacer) + padding['Left'] + 2
            width = x + barwidth - 1
            y =padding['Top']+availableHeight- float((d.init - min) * stepHeight / self.numofeachStep)
            height = y +float((d.init-d.close))*stepHeight/self.numofeachStep;
            draw.rectangle((x, y) + (width, height), fill="white" if d.isBull() else bellColor,
                           outline=(bullColor if d.isBull() else bellColor))
            if barwidth <3 :
                continue
            updownx = x + (barwidth / 2)
            if d.isBull():
                upheight = (d.max - d.close) * stepHeight / self.numofeachStep
                downheight = (d.init - d.min) * stepHeight / self.numofeachStep
                draw.line((updownx, height) + (updownx, height - upheight), fill=bullColor if d.isBull() else bellColor)
                draw.line((updownx, y) + (updownx, y + downheight), fill=bullColor if d.isBull() else bellColor)
            else:
                upheight = (d.max - d.init) * stepHeight / self.numofeachStep
                downheight = (d.close - d.min) * stepHeight / self.numofeachStep
                draw.line((updownx, y) + (updownx, y - upheight), fill=bullColor if d.isBull() else bellColor)
                draw.line((updownx, height) + (updownx, height + downheight), fill=bullColor if d.isBull() else bellColor)

    def __drawInfo(self,draw,minMax):
        stockName=self.todaydata['name']
        draw.text((self.padding['Left']+4,self.padding['Top']+5),text=stockName,fill=self.frameWorkText,font=self.font)
        pass

    def __drawFrameWork(self,draw, frameWorkColor, frameWorkText, imSize,  minMax, padding):
        imageWidth=imSize[0]
        imageHeight=imSize[1]
        availableHeight = (imageHeight - padding['Top'] - padding['Bottom'])*0.8
        min=minMax[0]
        max=minMax[1]
        #min = int(minMax[0] / self.numofeachStep) * self.numofeachStep
        #max = int(minMax[1] / self.numofeachStep + 1) * self.numofeachStep
        step = self.stepNum#int((max - min) / self.numofeachStep)
        stepHeight = int(availableHeight / step)
        for i in range(0, step + 1):
            x=padding['Left']
            y=padding['Top'] + availableHeight - stepHeight * i
            w=imageWidth - padding['Right']
            h=padding['Top'] + availableHeight - stepHeight * i
            draw.line(
                (x,y )
                + (w,h),
                fill=frameWorkColor)
            draw.text(
                (x-28,y - 6),
                "%0.2f" % (min + self.numofeachStep * i),
                fill=frameWorkText)


if __name__ == "__main__":
    sdf=StockDataFtecher()
    datas=sdf.fetchData()
    #datas=list(filter(lambda d:int(d.day)>20190101,datas))
    print(datas);
    sdf.drawImage(datas)

